﻿using System;
using System.Drawing;
using System.Text;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;
using Microsoft.Office.PowerPoint.STAMP.Properties;
using Font = System.Drawing.Font;
using Shape = Microsoft.Office.Interop.PowerPoint.Shape;

namespace Microsoft.Office.PowerPoint.STAMP.Core
{
    public static class Utils
    {
        #region Dimension and Font Defaults

        private const double NineThreeQuaterInch = 9.75;
        private const double EightHalfInch = 8.5;
        private const double SevenInch = 7;
        private const double SixInch = 6;
        private const double FiveInch = 5;
        private const double FourInch = 4;

        public const int Font30 = 30;
        public const int Font26 = 26;
        public const int Font21 = 21;
        public const int Font18 = 18;
        public const int Font16 = 16;
        public const int Font12 = 12;

        #endregion

        /// <summary>
        /// Returns the number of points per inch of the current display.
        /// Currently defaults to 72 PPI but should retrieve system settings.
        /// </summary>
        private static float PointsPerInch
        {
            get
            {
                // TODO: calculate value based on system call?  
                // what about multi monitor & display on different computer?
                return 72;
            }
        }

        /// <summary>
        /// Returns the 'default' font size based on the width of the display area.
        /// </summary>
        /// <param name="widthInPixels">The width in pixels of the area.</param>
        /// <returns>An integer point size for the font based on the width.</returns>
        public static int DetermineFontSize(double widthInPixels)
        {
            int fontSize = Font30;

            if (widthInPixels >= ConvertInchesToPixels(NineThreeQuaterInch))
                fontSize = Font30;
            else if (widthInPixels >= ConvertInchesToPixels(EightHalfInch))
                fontSize = Font26;
            else if (widthInPixels >= ConvertInchesToPixels(SevenInch))
                fontSize = Font21;
            else if (widthInPixels >= ConvertInchesToPixels(SixInch))
                fontSize = Font18;
            else if (widthInPixels >= ConvertInchesToPixels(FiveInch))
                fontSize = Font16;
            else if (widthInPixels >= ConvertInchesToPixels(FourInch))
                fontSize = Font12;
            else
                fontSize = Font30;

            return fontSize;
        }

        /// <summary>
        /// Converts the value in inches into pixels based on the current screen resolution.
        /// </summary>
        /// <param name="inches">The value to convert.</param>
        /// <returns>The value in pixels.</returns>
        private static double ConvertInchesToPixels(double inches)
        {
            return (inches * PointsPerInch);
        }

        /// <summary>
        /// Gets the Shape position based on the style and text
        /// </summary>
        /// <param name="slide">Slide that contains the shape.</param>
        /// <param name="mediaShape">Media shape that the captions are added to.</param>
        /// <param name="captionStyle">Caption style for the specific caption.</param>
        /// <param name="text">Caption text.</param>
        /// <returns>Returns the rectangle details.</returns>
        public static RectangleF GetShapePosition(Slide slide, Shape mediaShape, CaptionStyle captionStyle, string text)
        {
            RectangleF shapeRectangleF = new RectangleF();
            if (mediaShape.MediaType == PpMediaType.ppMediaTypeMovie)
            {
                if (captionStyle.FontSize == null)
                {
                    captionStyle.FontSize = GetFontSize(mediaShape.Width);
                }
            }
            else
            {
                if (captionStyle.FontSize == null)
                {
                    captionStyle.FontSize = GetFontSize(slide.Application.ActivePresentation.PageSetup.SlideWidth);
                }
            }

            captionStyle.CaptionLocation = GetCaptionLocation(mediaShape);

            // captionStyle.Font = ChangeFontSize(captionStyle.Font, captionStyle.FontSize);

            RectangleF mediaRectangle = new RectangleF(mediaShape.Left, mediaShape.Top, mediaShape.Width,
                                                       mediaShape.Height);
            RectangleF slideRectangle = new RectangleF(0, 0, slide.Application.ActivePresentation.PageSetup.SlideWidth,
                                                       slide.Application.ActivePresentation.PageSetup.SlideHeight);

            shapeRectangleF.Size = DetermineShapeSize(slideRectangle, mediaRectangle, captionStyle, text, slide);

            shapeRectangleF.Location = DetermineShapeLocation(slideRectangle, mediaRectangle, captionStyle.CaptionLocation,
                                                              shapeRectangleF.Size);

            return shapeRectangleF;
        }

        /// <summary>
        /// Gets the caption location.
        /// </summary>
        /// <param name="mediaShape">The media shape.</param>
        /// <returns></returns>
        public static CaptionLocation GetCaptionLocation(Shape mediaShape)
        {
            var loc = CaptionLocation.SlideBottom;
            if(mediaShape.MediaType == PpMediaType.ppMediaTypeMovie)
            {
                loc = CaptionLocation.VideoBottom;

                if (mediaShape.Width < ConvertInchesToPixels(FourInch))
                    loc = CaptionLocation.SlideBottom;
            }
            return loc;
        }
        private static Length GetFontSize(float width)
        {
            
            return new Length
                       {
                           Unit = LengthUnit.Pixel,
                           Value = DetermineFontSize(width)
                       };
        }


        public static Font GetFont(CaptionStyle style)
        {
            Font font = new Font(style.FontFamily, (float)style.FontSize.ToPixelLength(), style.FontWeight | style.FontStyle);
            return font;
        }

        /// <summary>
        /// Determines the shape location based on the CaptionLocation property
        /// </summary>
        /// <param name="slideRectangle"></param>
        /// <param name="mediaRectangle"></param>
        /// <param name="captionStyle"></param>
        /// <param name="shapeSize"></param>
        /// <returns></returns>
        public static PointF DetermineShapeLocation(RectangleF slideRectangle, RectangleF mediaRectangle,
                                                     CaptionLocation captionLocation, SizeF shapeSize)
        {
            PointF pointF = new PointF();

            pointF.Y = DetermineShapeYPositionByLocation(captionLocation, slideRectangle, mediaRectangle,
                                                         shapeSize.Height);
            pointF.X = captionLocation == CaptionLocation.VideoBottom ? mediaRectangle.X : slideRectangle.X;

            return pointF;
        }

        /// <summary>
        /// Determines the Y / Top position of where to place the item by the CaptionLocation
        /// </summary>
        /// <param name="captionLocation"></param>
        /// <param name="slideRectangle"></param>
        /// <param name="mediaRectangle"></param>
        /// <param name="shapeHeight"></param>
        /// <returns></returns>
        public static float DetermineShapeYPositionByLocation(CaptionLocation captionLocation,
                                                               RectangleF slideRectangle, RectangleF mediaRectangle,
                                                               float shapeHeight)
        {
            float position = captionLocation == CaptionLocation.VideoBottom
                                 ? DetermineShapeYPosition(mediaRectangle.Height, mediaRectangle.Top, shapeHeight)
                                 : DetermineShapeYPosition(slideRectangle.Height, slideRectangle.Top, shapeHeight);
            return position;
        }

        /// <summary>
        /// Works out the Y / Top position so that the item will fit correctly at the bottom of the container 
        /// </summary>
        /// <param name="containerHeight"></param>
        /// <param name="top"></param>
        /// <param name="shapeHeight"></param>
        /// <returns></returns>
        public static float DetermineShapeYPosition(float containerHeight, float top, float shapeHeight)
        {
            float position = containerHeight + top - shapeHeight;
            return position;
        }

        /// <summary>
        /// Determines the size of the item
        /// </summary>
        /// <param name="slideRectangle"></param>
        /// <param name="mediaRectangle"></param>
        /// <param name="captionStyle"></param>
        /// <param name="text"></param>
        /// <param name="slide"></param>
        /// <returns></returns>
        public static SizeF DetermineShapeSize(RectangleF slideRectangle, RectangleF mediaRectangle,
                                               CaptionStyle captionStyle, string text, Slide slide)
        {
            SizeF shapeSize = new SizeF
                                  {
                                      Width = DetermineShapeWidth(captionStyle.CaptionLocation, slideRectangle.Width,
                                                                  mediaRectangle.Width)
                                  };

            SizeF textSize = GetTextboxShapeSizeF(slide, text, captionStyle, shapeSize.Width);

            shapeSize.Height = textSize.Height * DetermineNumberLines(textSize.Width, shapeSize.Width, captionStyle);

            return shapeSize;
        }

        /// <summary>
        /// Determines how many lines the text should have
        /// </summary>
        /// <param name="textWidth">Width of the text.</param>
        /// <param name="shapeWidth">Width of the shape.</param>
        /// <param name="captionStyle">The caption style.</param>
        /// <returns></returns>
        internal static int DetermineNumberLines(float textWidth, float shapeWidth, CaptionStyle captionStyle)
        {
            double numLines = 1;
            if (textWidth > shapeWidth)
            {
                numLines = Math.Ceiling(textWidth / shapeWidth);
            }

            if (numLines > captionStyle.CaptionLines)
            {
                numLines = captionStyle.CaptionLines;
            }

            if (captionStyle.CaptionDisplayArea)
            {
                numLines = captionStyle.CaptionLines;
            }

            return Convert.ToInt32(numLines);
        }

        /// <summary>
        /// Checks which width to use based on the caption location element
        /// </summary>
        /// <param name="captionLocation"></param>
        /// <param name="slideWidth"></param>
        /// <param name="mediaWidth"></param>
        /// <returns></returns>
        public static float DetermineShapeWidth(CaptionLocation captionLocation, float slideWidth, float mediaWidth)
        {
            float width = captionLocation == CaptionLocation.VideoBottom
                              ? mediaWidth
                              : slideWidth;
            return width;
        }

        /// <summary>
        /// Gets the shapes name dropping the filetype
        /// </summary>
        /// <param name="shapeFullName"></param>
        /// <returns></returns>
        public static string GetShapeName(string shapeFullName)
        {
            return shapeFullName.Substring(0, shapeFullName.LastIndexOf("."));
        }

        /// <summary>
        /// Creates the name of the shape.
        /// </summary>
        /// <param name="mediaName">Name of the media.</param>
        /// <param name="captionText">The caption text.</param>
        /// <returns></returns>
        public static string CreateShapeName(string mediaName, string captionText)
        {
            return CreateShapeName(mediaName, captionText, null, null);
        }

        /// <summary>
        /// Overloaded utility method for creating the shape names
        /// </summary>
        /// <param name="mediaName"></param>
        /// <param name="captionText"></param>
        /// <param name="begin"></param>
        /// <returns></returns>
        public static string CreateShapeName(string mediaName, string captionText, int begin)
        {
            return CreateShapeName(mediaName, captionText, begin, null);
        }

     
        /// <summary>
        /// Creates the shapes name from the media name, caption text first 3 words,
        /// begin and end time of the animation
        /// </summary>
        /// <param name="mediaName"></param>
        /// <param name="captionText"></param>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public static string CreateShapeName(string mediaName, string captionText, int? begin, int? end)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(mediaName);

            if (!String.IsNullOrEmpty(captionText))
            {
                string[] words = captionText.Split(' ');
                for (int i = 0; i < words.Length; i++)
                {
                    if (i == 3)
                    {
                        break;
                    }

                    sb.Append(words[i]);
                }
            }

            if (begin.HasValue)
            {
                sb.Append(begin.Value.ToTimeSpanString(Resources.TimeSpanStringFormat));
            }

            if (begin.HasValue && end.HasValue)
            {
                sb.Append("-");
            }

            if (end.HasValue)
            {
                sb.Append(end.Value.ToTimeSpanString(Resources.TimeSpanStringFormat));
            }

            return sb.ToString();
        }

        /// <summary>
        /// Firsts the three words.
        /// </summary>
        /// <param name="captionText">The caption text.</param>
        /// <returns></returns>
        public static string FirstThreeWords(string captionText)
        {
            StringBuilder sb = new StringBuilder();
            if (!String.IsNullOrEmpty(captionText))
            {
                string[] words = captionText.Split(' ');
                for (int i = 0; i < words.Length; i++)
                {
                    if (i == 3)
                    {
                        break;
                    }

                    sb.Append(words[i] + " ");
                }
            }
            return sb.ToString();
        }

        private static Shape _tempShape;

        /// <summary>
        /// Creates a dummy Shape Textbox to get the size attributes from it.
        /// </summary>
        /// <param name="slide">The slide.</param>
        /// <param name="text">The text.</param>
        /// <param name="captionStyle">The caption style.</param>
        /// <param name="width"></param>
        /// <returns></returns>
        public static SizeF GetTextboxShapeSizeF(Slide slide, string text, CaptionStyle captionStyle, float width)
        {
            //Shape tempShape = UtilPowerPoint.AddTextboxShape(slide, 0, 0, 350, 50);
            if (_tempShape == null)
                _tempShape = UtilPowerPoint.AddTextboxShape(slide, 0, 0, 350, 50);


            _tempShape.TextFrame.TextRange.Font.Bold = captionStyle.FontWeight == FontStyle.Bold
                                                     ? MsoTriState.msoTrue
                                                     : MsoTriState.msoFalse;
            _tempShape.TextFrame.TextRange.Font.Italic = captionStyle.FontStyle == FontStyle.Italic
                                                        ? MsoTriState.msoTrue
                                                        : MsoTriState.msoFalse;
            _tempShape.TextFrame.TextRange.Font.Underline = captionStyle.TextDecorationStyleProperty ==
                                                       TextDecorationAttributeValue.underline
                                                           ? MsoTriState.msoTrue
                                                           : MsoTriState.msoFalse;
            var font = GetFont(captionStyle);

            _tempShape.Visible = MsoTriState.msoFalse;
            _tempShape.TextFrame.TextRange.Text = text;
            _tempShape.TextFrame.TextRange.Font.Name = font.Name;
            _tempShape.TextFrame.TextRange.Font.Size = font.Size;
            _tempShape.TextFrame.MarginBottom = 0;
            _tempShape.TextFrame.MarginTop = 0;
            //_tempShape.TextFrame.m

            _tempShape.TextFrame.WordWrap = MsoTriState.msoFalse;
            _tempShape.TextFrame.AutoSize = PpAutoSize.ppAutoSizeShapeToFitText;

            SizeF sizeF = new SizeF(_tempShape.Width, _tempShape.Height);

            _tempShape.Width = width;

            return sizeF;
        }

        /// <summary>
        /// Truncates the caption text.
        /// </summary>
        /// <param name="slide">The slide.</param>
        /// <param name="caption">The caption.</param>
        /// <param name="shapeWidth">Width of the shape.</param>
        /// <returns></returns>
        public static string TruncateCaptionText(Slide slide, CaptionItem caption, float shapeWidth)
        {
            SizeF textSize = GetTextboxShapeSizeF(slide, caption.Text, caption.CaptionStyle, shapeWidth);

            var numLines = textSize.Width / shapeWidth;

            if (numLines > caption.CaptionStyle.CaptionLines)
            {
                int txtLength = caption.Text.Length;
                double trimAmount = (Math.Floor(txtLength / numLines)) * caption.CaptionStyle.CaptionLines;

                caption.Text = caption.Text.TrimWords(Convert.ToInt32((trimAmount)));
            }

            return caption.Text;
        }
    }
}